package 注解;

public class 注解的字节码结构 {

}
注解
一 、描述
1.1 methods 方法表
2.2 annotations的数据结构
2.2.1 RuntimeVisibleAnnotations
2.2.1.1 annotation的结构
二、字节码
2.1 源码
2.2 编译后的bytecode
注解可以标注在class、field、method上，同时又可以分为编译期的注解，运行期的注解。
这篇文章会结合jvm规范3.1.5说明注解在字节码中的位置。

一 、描述
注解可以标注在class、interface、fileds、method、method parameters、type parameters。注解甚至可以作用在包级别package。
类似的所有的组件基本上都有一个Attribute区域，注解是一种Attribute被放置在部分。
使用method的字节码来举例

1.1 methods 方法表
方法表在class文件的位置

ClassFile {
    u4             magic;
    u2             minor_version;
    u2             major_version;
    u2             constant_pool_count;
    cp_info        constant_pool[constant_pool_count-1];
    u2             access_flags;
    u2             this_class;
    u2             super_class;
    u2             interfaces_count;
    u2             interfaces[interfaces_count];
    u2             fields_count;
    field_info     fields[fields_count];
    // 这里 
    u2             methods_count;
    method_info    methods[methods_count];
    // 结束
    u2             attributes_count;
    attribute_info attributes[attributes_count];
}


method_info的的数据结构

method_info {
    u2             access_flags;
    u2             name_index;
    u2             descriptor_index; 
    // 这里
    u2             attributes_count;
    attribute_info attributes[attributes_count];
    // 结束
}

Attribute的数据结构
属性是变长的，种类有很多。分为jvm规范中预制的，也有各厂家自定义的。但是有一个统一的格式。

attribute_info {
    u2 attribute_name_index;
    u4 attribute_length;
    u1 info[attribute_length];
}

2.2 annotations的数据结构
除了方法表之外，很多地方都有一个属性表。注解作为一个属性被放在这个位置。
注解属性结构细分有7种

type	descriptor
RuntimeVisibleAnnotations	运行时可见
RuntimeVisibleAnnotations	运行时不可见
RuntimeVisibleParameterAnnotations	标注在参数上，运行时可见的参数型注解
RuntimeInvisibleParameterAnnotations	标注在参数上，运行时不可见的参数型注解
RuntimeVisibleTypeAnnotations	标注在例行上比如泛型，运行时可见的类型注解
RuntimeInvisibleTypeAnnotations	标注在例行上比如泛型，运行时不可见的类型注解
AnnotationDefault	注解的默认值
2.2.1 RuntimeVisibleAnnotations
顾名思议，就是运行期(runtime) 可见的。延伸一点，运行期可以通过函数等方式获取到的。
注解表结构

RuntimeVisibleAnnotations_attribute {
    u2         attribute_name_index;
    u4         attribute_length;
    u2         num_annotations;
    annotation annotations[num_annotations];
}

2.2.1.1 annotation的结构
继上一步，理解注解的结构和注解代码定义是分不开的

annotation 代码定义

这个是一个完整的定义

@Retention(RetentionPolicy.RUNTIME)
//@Retention(RetentionPolicy.SOURCE)
@Target(ElementType.METHOD)
public @interface Info {
    // value
    int age();
    String name();
    String descriptor() default "simple annotation";

}

一个注解可以继承注解（@Retention、@Target）
也需要定义字节的成员（int age();）

使用时如下，这是一个打印了Info注解的方法

public class TestInfo {
    @Info(age = 0, name = "baby")
    public void born() {
        System.out.println("human born");
    }

    public static void main(String[] args) throws Exception {
        TestInfo ti = new TestInfo();
        ti.born();
        Method born = TestInfo.class.getMethod("born");
        if (born.isAnnotationPresent(Info.class)) {
            Info info = born.getAnnotation(Info.class);
            System.out.println(info.age());
            System.out.println(info.name());
            System.out.println(info.descriptor());
        }

    }
}

annotation 的结构定义
对照上面的例子可理解annotation中包含一堆 element_value 元素的

annotation {
    u2 type_index;
    // element_value 的数量
    u2 num_element_value_pairs;
    {   u2  element_name_index;
        element_value value;
    } element_value_pairs[num_element_value_pairs];
}

element_value 的结构定义
这里大概解释下成员，后面会有字节码的例子来分析。

element_value {
    u1 tag;
    union {
    	// 基本类型或者 String 的索引，值对应着常量池`constant_pool `
        u2 const_value_index;
		// 枚举类型的键值对
        {   u2 type_name_index;
            u2 const_name_index;
        } enum_const_value;
		// 类型的信息
        u2 class_info_index;
		//嵌套注解，注解的复合
        annotation annotation_value;
		//数组类型的元数据
        {   u2            num_values;
            element_value values[num_values];
        } array_value;
    } value;
}

其他的就不讲了，可以看jvm规范，重点是理解

二、字节码
前面讲了注解在字节码位置，和注解的数据结构。这里看一个具体的例子

2.1 源码
来描述学校类型的注解

public @interface School {
    String type() default "highschool";
}

描述学生信息的注解
这个注解很复杂

复合注解（元注解+自定义注解）
基本类型的成员 int
复杂类型的成员 String
带有默认值的成员 default
基本类型数组的成员 int[]
复杂类型数组的成员 String[]
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
@School
public @interface StudentInfo {
    // value
    int age();

    String name();

    String descriptor() default "simple student";

    String[] course() default {"math", "language"};

    int[] scores() default {70, 70};

}

使用，born方法上使用了注解

public class TestInfo {
    @StudentInfo(age = 0, name = "baby")
    public void born() {
        System.out.println("human born");
    }


    public static void main(String[] args) throws Exception {
        TestInfo ti = new TestInfo();
        ti.born();
        Method born = TestInfo.class.getMethod("born");
        if (born.isAnnotationPresent(StudentInfo.class)) {
            StudentInfo info = born.getAnnotation(StudentInfo.class);
            System.out.println(info.age());
            System.out.println(info.name());
            System.out.println(info.descriptor());
        }

    }
}

2.2 编译后的bytecode
这里仅仅是class文件。但是jvm加载后真实的运行时代码，是有和之前说的数据结构是一致的。
TestInfo.born()
可以看到将born包含注解属性

  // access flags 0x1
  public born()V
  @Lbytecode/annotation/StudentInfo;(age=0, name="baby")
   L0
    LINENUMBER 8 L0
    GETSTATIC java/lang/System.out : Ljava/io/PrintStream;
    LDC "human born"
    INVOKEVIRTUAL java/io/PrintStream.println (Ljava/lang/String;)V
   L1
    LINENUMBER 9 L1
    RETURN
   L2
    LOCALVARIABLE this Lbytecode/annotation/TestInfo; L0 L2 0
    MAXSTACK = 2
    MAXLOCALS = 1

StudentInfo注解
这个注解的字节码，可以和上面提到的注解对照着看，发现这个文件就是依据上面的格式，从字节码中解析出来的。

Classfile /Users/xxxxxxx/Desktop/Work/mycode/Javadetail/BYTECODE/bytecode/target/classes/bytecode/annotation/StudentInfo.class
  Last modified 2020-7-4; size 711 bytes
  MD5 checksum 6ccdff3fb70232c700d1d9766c521339
  Compiled from "StudentInfo.java"
public interface bytecode.annotation.StudentInfo extends java.lang.annotation.Annotation
  minor version: 0
  major version: 52
  flags: ACC_PUBLIC, ACC_INTERFACE, ACC_ABSTRACT, ACC_ANNOTATION
Constant pool:
   #1 = Class              #30            // bytecode/annotation/StudentInfo
   #2 = Class              #31            // java/lang/Object
   #3 = Class              #32            // java/lang/annotation/Annotation
   #4 = Utf8               age
   #5 = Utf8               ()I
   #6 = Utf8               name
   #7 = Utf8               ()Ljava/lang/String;
   #8 = Utf8               descriptor
   #9 = Utf8               AnnotationDefault
  #10 = Utf8               simple student
  #11 = Utf8               course
  #12 = Utf8               ()[Ljava/lang/String;
  #13 = Utf8               math
  #14 = Utf8               language
  #15 = Utf8               scores
  #16 = Utf8               ()[I
  #17 = Integer            70
  #18 = Utf8               SourceFile
  #19 = Utf8               StudentInfo.java
  #20 = Utf8               RuntimeVisibleAnnotations
  #21 = Utf8               Ljava/lang/annotation/Retention;
  #22 = Utf8               value
  #23 = Utf8               Ljava/lang/annotation/RetentionPolicy;
  #24 = Utf8               RUNTIME
  #25 = Utf8               Ljava/lang/annotation/Target;
  #26 = Utf8               Ljava/lang/annotation/ElementType;
  #27 = Utf8               METHOD
  #28 = Utf8               RuntimeInvisibleAnnotations
  #29 = Utf8               Lbytecode/annotation/School;
  #30 = Utf8               bytecode/annotation/StudentInfo
  #31 = Utf8               java/lang/Object
  #32 = Utf8               java/lang/annotation/Annotation
{
  public abstract int age();
    descriptor: ()I
    flags: ACC_PUBLIC, ACC_ABSTRACT

  public abstract java.lang.String name();
    descriptor: ()Ljava/lang/String;
    flags: ACC_PUBLIC, ACC_ABSTRACT

  public abstract java.lang.String descriptor();
    descriptor: ()Ljava/lang/String;
    flags: ACC_PUBLIC, ACC_ABSTRACT
    AnnotationDefault:
      default_value: s#10
  public abstract java.lang.String[] course();
    descriptor: ()[Ljava/lang/String;
    flags: ACC_PUBLIC, ACC_ABSTRACT
    AnnotationDefault:
      default_value: [s#13,s#14]
  public abstract int[] scores();
    descriptor: ()[I
    flags: ACC_PUBLIC, ACC_ABSTRACT
    AnnotationDefault:
      default_value: [I#17,I#17]}
SourceFile: "StudentInfo.java"
RuntimeVisibleAnnotations:
  0: #21(#22=e#23.#24)
  1: #25(#22=[e#26.#27])
RuntimeInvisibleAnnotations:
  0: #29()
